using System;
using System.Collections.Generic;
using System.Reflection;
using NetMigration.Factories;
using NetMigration.Services;

namespace NetMigration.Utilities
{
    public class MigrationExecuter
    {
        private readonly IMigrationFactory factory;
        private readonly IVersionService versionService;

        public MigrationExecuter()
        {
        }

        public MigrationExecuter(IMigrationFactory factory)
        {
            this.factory = factory;
            versionService = factory.CreateVersionController();
        }

        public void Migrate(Assembly targetAssembly)
        {
            if (targetAssembly == null)
            {
                return;
            }
            int currentVersion = versionService.GetCurrentVersion();
            List<Type> migrationTypeList = targetAssembly.AllMigrationTypes().FindAllVersionLaterThan(currentVersion);
            migrationTypeList.ForEach(UpMigration);
            CreateOrUpdateVersion(migrationTypeList);
        }

        public void Migrate(Assembly targetAssembly, int targetVersion)
        {
            if (targetAssembly == null)
            {
                return;
            }
            int currentVersion = versionService.GetCurrentVersion();
            List<Type> migrationTypeList = targetAssembly.AllMigrationTypes().FindAllVersionInRange(targetVersion, currentVersion + 1);
            migrationTypeList.Reverse();
            migrationTypeList.ForEach(DownMigration);
            versionService.InsertOrUpdateVersion(targetVersion);
        }

        private void CreateOrUpdateVersion(List<Type> migrationTypeList)
        {
            int version = migrationTypeList.MaxVersion();
            versionService.InsertOrUpdateVersion(version);
        }

        private void UpMigration(Type migrationType)
        {
            Migration migration = GetMigration(migrationType);
            migration.Up();
        }

        private void DownMigration(Type migrationType)
        {
            Migration migration = GetMigration(migrationType);
            migration.Down();
        }

        private Migration GetMigration(Type migrationType)
        {
            var migration = (Migration) Activator.CreateInstance(migrationType);
            migration.MigrationFactory = factory;
            return migration;
        }
    }
}